package ar.edu.itba.it.atlc.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import ar.edu.itba.it.atlc.exception.ParseException;

public abstract class Transition {

	public static enum Symbol {
		Symbol_0(0), Symbol_1(1), Symbol_2(2);

		private final int value;

		Symbol(int value) {
			this.value = value;
		}

		public int getValue() {
			return value;
		}

		@Override
		public String toString() {
			return String.valueOf(getValue());
		}

		public static Symbol parse(String value) {
			for (Symbol s : Symbol.values()) {
				if (s.getValue() == Integer.valueOf(value)) {
					return s;
				}
			}
			throw new ParseException();
		}

	}

	public static enum Movement {
		LEFT("<"), RIGHT(">");

		private final String value;

		Movement(String value) {
			this.value = value;
		}

		public String getValue() {
			return value;
		}

		public static Movement parse(String value) {
			for (Movement m : Movement.values()) {
				if (m.getValue().equals(value)) {
					return m;
				}
			}
			throw new ParseException();
		}

	}

	protected final Status origin;
	protected final Symbol symbol;
	protected final Status destination;

	public Transition(Status origin, Symbol symbol, Status destination) {
		this.origin = origin;
		this.symbol = symbol;
		this.destination = destination;
	}

	public Status getOrigin() {
		return origin;
	}

	public Symbol getSymbol() {
		return symbol;
	}

	public Status getDestination() {
		return destination;
	}

	public static Status parse(String line,
			Map<Status, List<Transition>> transitions) {
		Status s1 = null;
		Status s2 = null;
		Transition t = null;
		try {
			String[] args = line.split(",");
			if (args.length != 4) {
				throw new ParseException();
			}
			s1 = getStatus(args[0], transitions);
			s2 = getStatus(args[2], transitions);
			try {
				Integer.parseInt(args[3]);
				t = new WriteTransition(new Status(args[0]), Symbol
						.parse(args[1]), new Status(args[2]), Symbol
						.parse(args[3]));
			} catch (NumberFormatException e) {
				t = new MovementTransition(new Status(args[0]), Symbol
						.parse(args[1]), new Status(args[2]), Movement
						.parse(args[3]));
			}

		} catch (Exception e) {
			throw new ParseException("Transici�n inv�lida.");
		}
		if (transitions.containsKey(s1)) {
			if (transitions.get(s1).contains(t)) {
				System.err.println("La transici�n: " + t + " es ambigua.");
				System.exit(1);
			}
			transitions.get(s1).add(t);
		} else {
			List<Transition> list = new ArrayList<Transition>();
			list.add(t);
			transitions.put(s1, list);
		}
		if (!transitions.containsKey(s2)) {
			transitions.put(s2, new ArrayList<Transition>());
		}
		return s1;
	}

	private static Status getStatus(String string,
			Map<Status, List<Transition>> transitions) {
		Status status = null;
		for (Status s : transitions.keySet()) {
			if (s.getName().equals(string))
				status = s;
		}
		if (status == null) {
			status = new Status(string);
		}
		return status;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((origin == null) ? 0 : origin.hashCode());
		result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Transition other = (Transition) obj;
		if (origin == null) {
			if (other.origin != null)
				return false;
		} else if (!origin.equals(other.origin))
			return false;
		if (symbol != other.symbol)
			return false;
		return true;
	}

}
